﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading;
using MySql.Data.MySqlClient;

namespace DataServer.Dao
{

    public static class ConnectionPool
    {
        private static readonly object _locker = new object();
        private static readonly string _connStr = ConfigurationManager.ConnectionStrings["MyContext"].ConnectionString;
        private static string databaseName = "central_control";
        private static Dictionary<string, MySqlConnection> Connections;

        private static readonly Dictionary<string, ConnectState> ConnectBusy = new Dictionary<string, ConnectState>();
        // ReSharper disable once NotAccessedField.Local
        private static Timer _timer;

        static ConnectionPool()
        {
            _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromHours(1));
        }
        private static void DoWork(object state)
        {
            try
            {
                lock (_locker)
                {
                    List<string> removeList = new List<string>();
                    foreach (KeyValuePair<string, ConnectState> pair in ConnectBusy)
                    {
                        if (!pair.Value.State && (DateTime.Now - pair.Value.LastAccessTime).TotalHours > 1)
                        {
                            if (Connections.ContainsKey(pair.Key))
                            {
                                Connections[pair.Key].CloseAsync();
                                Connections.Remove(pair.Key);
                                removeList.Add(pair.Key);
                            }
                        }
                    }
                    foreach (string s in removeList)
                    {
                        ConnectBusy.Remove(s);
                    }
                }
            }
            catch
            {

            }
        }
        public static ConnectResult GetConnection()
        {
            if (string.IsNullOrEmpty(databaseName))
                return null;
            if (Connections == null)
            {
                lock (_locker)
                {
                    Connections = new Dictionary<string, MySqlConnection>();
                }
            }
            return FindFreeSqlConnection(databaseName);
        }

        public static void ReleaseConnection(string key)
        {
            lock (_locker)
            {
                if (ConnectBusy.ContainsKey(key))
                {
                    ConnectBusy[key].State = false;
                    ConnectBusy[key].LastAccessTime = DateTime.Now;
                }
            }
        }
        private static ConnectResult FindFreeSqlConnection(string database)
        {
            lock (_locker)
            {
                var keys = Connections.Keys.Where(sss => sss.StartsWith(database));
                IEnumerable<string> enumerable = keys as string[] ?? keys.ToArray();
                if (enumerable.Any())
                {
                    foreach (var key in enumerable)
                    {
                        if (ConnectBusy.ContainsKey(key) && !ConnectBusy[key].State)
                        {
                            if (Connections[key].State == ConnectionState.Open)
                            {
                                ConnectBusy[key].State = true;
                                return new ConnectResult(key, Connections[key]);
                            }
                            else if (Connections[key].State == ConnectionState.Closed)
                            {
                                Connections[key].Open();
                                ConnectBusy[key].State = true;
                                return new ConnectResult(key, Connections[key]);
                            }
                        }
                    }
                }
                int poolSize = GetConnectionPoolSize();
                string strconn = GetConnectionString();
                if (enumerable.Count() < poolSize)
                {
                    MySqlConnection conn = new MySqlConnection(strconn);
                    conn.Open();
                    for (int i = 0; i < poolSize; i++)
                    {
                        string newkey = database + "_" + i;
                        if (!Connections.ContainsKey(newkey))
                        {
                            Connections.Add(newkey, conn);
                            ConnectBusy[newkey] = new ConnectState() { State = true, LastAccessTime = DateTime.Now };
                            return new ConnectResult(newkey, Connections[newkey]);
                        }
                    }
                }
            }

            return null;
        }

        public static int GetConnectionPoolSize()
        {
            return 50;
        }
        public static string GetConnectionString()
        {
            return _connStr;
        }
    }

    public class ConnectResult
    {
        public string Name { get; set; }
        public MySqlConnection Connection { get; set; }

        public ConnectResult()
        {

        }
        public ConnectResult(string name, MySqlConnection connection)
        {
            this.Name = name;
            this.Connection = connection;
        }
    }

    public class ConnectState
    {
        public bool State { get; set; }
        public DateTime LastAccessTime { get; set; }
    }
}
